// YemuPrint
#include <stdarg.h>
#include "YemuEnv.h"

#define UART_TX_ADDR    (0x10000000)
#define UART_RX_ADDR    (0x10000004)

void PUTCHAR(char ch) {
    *((char*)UART_TX_ADDR) = ch;
    return ;
}

void PUTSTR(const char* str) {
    int i = 0;
    while (str[i] != '\0' ) {
        PUTCHAR(str[i]);
        i++;
    }
    return ;
}

void PUTUINT(unsigned long num, int base) {
    char buf[65];
    int i = 0;
    if (num == 0) {
        PUTCHAR('0');
        return ;
    }
    while (num != 0) {
        int d = num % base;
        buf[i++] = (d < 10) ? (d + '0') : (d - 10 + 'A');
        num /= base;
    }
    while (i--) PUTCHAR(buf[i]);
    return ;
}

void PUTINT(long num, int base) {
    if (num < 0) {
        PUTCHAR('-');
        PUTUINT((unsigned long)(-(num + 1)) + 1, base); // -(long)INT_MIN may overflow
    } else {
        PUTUINT((unsigned long)num, base);
    }
}

void YemuPrint(const char* fmt, ...) {
    va_list argptr;
    va_start(argptr, fmt);

    int i = 0;
    while(fmt[i] != '\0') {
        
        if (fmt[i] != '%') {
            PUTCHAR(fmt[i++]);
            continue;
        }

        i++;
        int longflag = 0;
        if (fmt[i] == 'l') {
            longflag = 1;
            i++;
        }
        
        switch(fmt[i]) {
            case 'c':
                //‘char’is promoted to ‘int’ when passed through ‘...’
                PUTCHAR(va_arg(argptr, int));
                break;
            case 's':
                PUTSTR(va_arg(argptr, char*));
                break;
            case 'd':
                if (longflag) PUTINT(va_arg(argptr, long), 10);
                else          PUTINT(va_arg(argptr, int), 10);
                break;
            case 'u':
                if (longflag) PUTUINT(va_arg(argptr, unsigned long), 10);
                else          PUTUINT(va_arg(argptr, unsigned int), 10);
                break;
            case 'x':
                if (longflag) PUTUINT(va_arg(argptr, unsigned long), 16);
                else          PUTUINT(va_arg(argptr, unsigned int), 16);
                break;
            case '%':
                PUTCHAR('%');
                break;
            default:
                PUTCHAR('%');
                PUTCHAR(fmt[i]);
                break;
        }
        i++;
    }
    va_end(argptr);
    return ;
}
